﻿using Graphviz4Net.Graphs;
using Graphviz4Net.WPF.Example;
using LogAnalyzer.Model;
using LogAnalyzer.View;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Numerics;
using System.Text;
using System.Windows;

namespace LogAnalyzer.ViewModel
{
    public class GraphViewModel : IGraphViewModel
    {
        public event EventHandler loadJoinGraph;

        public bool MnuRoute { get { return true; } }

        public bool MnuJoin { get { return true; } }

        public bool MnuAddJoin { get { return false; } }

        public void OnLoadJoinGraph(Node node)
        {
            if (loadJoinGraph != null)
            {
                loadJoinGraph(node, new EventArgs());
            }
        }

        public Graph<GraphComponent> Graph { get; private set; }

        public string Header { get; set; }

        public string key { get; set; }

        public GraphViewModel(RoutingData row)
        {
            Graph = new Graph<GraphComponent>();
            var route = App.Log.Where(x => (x.what.content != null ? x.what.content.id : "0") == row.RouteId && x.what.type == Util.MessageType.Route).OrderBy(x => x.time).ToList();

            key = route[0].what.content.key;

            Node from = null;
            Edge<GraphComponent> edge = null;
            foreach (var r in route)
            {
                var msg = App.Log.FirstOrDefault(x => x.what.type == Util.MessageType.StartSending && x.to.address == r.to.address);
                r.to.leaf = msg.what.state.leaf;
                r.to.table = msg.what.state.table;
                Node currentNode = r.to.Clone();
                this.Graph.AddVertex(currentNode);

                currentNode.showLeaf += to_showLeaf;
                currentNode.showTable += to_showTable;

                int next = int.Parse(r.what.content.routeCount) + 1;

                var nextMsg = route.FirstOrDefault(x => x.what.content.routeCount == next.ToString());
                if (nextMsg != null)
                {
                    var fromId = currentNode.nodeId;
                    var toId = nextMsg.to.nodeId;
                    var keyId = r.what.content.key;

                    var n = IsThereCandidateForTable(toId, keyId, next - 1);
                    if (n != null)
                    {
                        currentNode.MissingFromTable = true;
                        currentNode.missingNode = n.nodeId;
                        if (edge != null)
                        {
                            edge.Color = "Red";
                        }
                        currentNode.Color = "Red";
                    }
                    else 
                    {
                        n = BetterCandidateForLeaf(toId, keyId);
                        if (n != null)
                        {
                            currentNode.MissingFromLeaf = true;
                            currentNode.missingNode = n.nodeId;
                            if (edge != null)
                            {
                                edge.Color = "Red";
                            }
                            currentNode.Color = "Red";
                        }
                    }
                }
                else
                {
                    var toId = r.to.nodeId;
                    var keyId = r.what.content.key;

                    if (toId != keyId)
                    {
                        currentNode.MissingFromLeaf = true;
                        currentNode.missingNode = key;
                        if (edge != null)
                        {
                            edge.Color = "Red";
                        }
                        currentNode.Color = "Red";
                    }
                }

                if (r.what.content.routeCount != "0")
                {
                    edge = new Edge<GraphComponent>(from, currentNode, new Arrow());
                    this.Graph.AddEdge(edge);
                }

                from = currentNode;
            }
        }

        void to_showTable(object sender, EventArgs e)
        {
            var node = sender as Node;
            var routingInfoView = new RoutingInfoView();

            string to = "?";

            var edge = Graph.Edges.FirstOrDefault(x => x.Source == node);
            if (edge != null)
            {
                to = ((Node)edge.Destination).nodeId;
            }

            var routingInfoViewModel = new RoutingInfoViewModel(node.leaf, node.tableData, node, to, key);
            routingInfoView.DataContext = routingInfoViewModel;

            Window window = new Window
            {
                Title = "Node Routing Info " + node.nodeId,
                Content = routingInfoView
            };

            window.Show();
        }

        void to_showLeaf(object sender, EventArgs e)
        {
            OnLoadJoinGraph((Node)sender);
        }

        private Node BetterCandidateForLeaf(string to, string key)
        {
            BigInteger b = BigInteger.Parse(Util.AddZero(to), System.Globalization.NumberStyles.HexNumber);
            BigInteger k = BigInteger.Parse(Util.AddZero(key), System.Globalization.NumberStyles.HexNumber); 

            foreach (var n in App.Nodes)
            {
                if (n.nodeId != key)
                {
                    BigInteger a = BigInteger.Parse(Util.AddZero(n.nodeId), System.Globalization.NumberStyles.HexNumber);
                    BigInteger c1 = BigInteger.Abs(a - k);
                    BigInteger c2 = BigInteger.Abs(b - k);
                    if (c1 < c2)
                    {
                        return n;
                    }
                }
            }

            return null;
        }

        private Node IsThereCandidateForTable(string to, string key, int step)
        {
            string subOfKey = key.Substring(0, step+1);
            string subOfTo = to.Substring(0, step+1);
            
            if (subOfKey != subOfTo)
            {
                foreach (var node in App.Nodes)
                {
                    if (node.nodeId != key && node.nodeId.StartsWith(subOfKey))
                    {
                        return node;
                    }
                }
            }

            return null;
        }
    }
}
